/*
Copyright 2010,2011 Matt Van Der Westhuizen

This file is part of CGLL.

CGLL is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

CGLL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with CGLL.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.chaoticengine.cgll.entity.component.move;

import org.chaoticengine.cgll.entity.component.Command;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.state.StateBasedGame;
import org.simpleframework.xml.Element;

/**
 * The DirectionalMovementComponent is designed to model movement where the
 * entity rotates around its center and applies forward or backward thrust to
 * move itself. This model is very well suited to AI movement as it is fairly
 * easy to figure out what the AI should do using simple vector mathematics.
 *
 * @author Matt v.d. Westhuizen
 *
 */
public class DirectionalMovementComponent extends BasicMovementComponent {
    /** Turn rate in degrees per second. */
    protected float turnRate = 1.0f;

    /** Amount of turning being applied at the moment. */
    protected float turn = 0.0f;

    /** Movement clipping rectangle. */
    protected Rectangle clip = null;

    protected boolean doClipping = false;

    public DirectionalMovementComponent() {
        super();

        this.commands.put("ROTATE CCW", new Command(this) {
            @Override
            public void doAction(int delta) {
                turn -= turnRate;
            }
        });

        this.commands.put("ROTATE CW", new Command(this) {
            @Override
            public void doAction(int delta) {
                turn += turnRate;
            }
        });

        this.commands.put("LEFT", new Command(this) {
            @Override
            public void doAction(int delta) {
                Vector2f dir = DirectionalMovementComponent.this.getOwner().getDirection().copy().negate().getPerpendicular();
                DirectionalMovementComponent.this.accelerate(dir);
            }
        });

        this.commands.put("RIGHT", new Command(this) {
            @Override
            public void doAction(int delta) {
                Vector2f dir = DirectionalMovementComponent.this.getOwner().getDirection().copy().getPerpendicular();
                DirectionalMovementComponent.this.accelerate(dir);
            }
        });

        this.commands.put("FORWARD", new Command(this) {
            @Override
            public void doAction(int delta) {
                DirectionalMovementComponent.this.accelerate();
            }
        });

        this.commands.put("BACKWARD", new Command(this) {
            @Override
            public void doAction(int delta) {
                DirectionalMovementComponent.this.accelerate(
                        DirectionalMovementComponent.this.getOwner().getDirection().copy().negate());
            }
        });
    }

    @Override
    public void update(GameContainer gc, StateBasedGame sb, int delta) {
        // Default clipping rectangle is the screen edges
        if (clip == null) {
            clip = new Rectangle(owner.getBounds().getWidth() / 2.0f,
                    owner.getBounds().getHeight() / 2.0f,
                    gc.getWidth(),
                    gc.getHeight());
        }

        // Do clipping
        if (doClipping) {
            Vector2f pos = owner.getPosition();
            Shape bounds = owner.getBounds();

            if (pos.getX() + bounds.getWidth() > clip.getMaxX()) {
                pos.set(clip.getMaxX() - bounds.getWidth(), pos.getY());
                v.add(new Vector2f(-v.x, 0.0f));
            } else if (pos.getX() < clip.getMinX()) {
                pos.set(clip.getMinX(), pos.getY());
                v.add(new Vector2f(-v.x, 0.0f));
            }

            if (pos.getY() + bounds.getHeight() > clip.getMaxY()) {
                pos.set(pos.getX(), clip.getMaxY() - bounds.getHeight());
                v.add(new Vector2f(0.0f, -v.y));
            } else if (pos.getY() < clip.getMinY()) {
                pos.set(pos.getX(), clip.getMinY());
                v.add(new Vector2f(0.0f, -v.y));
            }

            owner.setPosition(pos);
        }

        // Turn the ship
        if (turn != 0) {
            this.getOwner().setRotation(this.getOwner().getRotation() + turn * (delta / 1000.0f));

            turn = 0;
        }

        // Thrust will be applied by BasicMovementComponent
        super.update(gc, sb, delta);
    }

    @Override
    public void onSetOwner() {
        super.onSetOwner();

        v = getOwner().getDirection().copy().normalise().scale(maxV);
    }

    @Element(name="turn_rate")
    public float getTurnRate() {
        return turnRate;
    }

    @Element(name="turn_rate")
    public void setTurnRate(float turnRate) {
        this.turnRate = turnRate;
    }

    @Element(name="clip", required=false)
    public boolean isClipping() {
        return doClipping;
    }

    @Element(name="clip", required=false)
    public void setClipping(boolean doClipping) {
        this.doClipping = doClipping;
    }


}
